/*
 * Wazuh Vulnerability Scanner - Unit Tests
 * Copyright (C) 2015, Wazuh Inc.
 * December 13, 2023.
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation.
 */

#include "osDataCache_test.hpp"
#include "MockSocketDBWrapper.hpp"
#include "TrampolineSocketDBWrapper.hpp"

TEST_F(OsDataCacheTest, TestSetAndGetSuccess)
{
    OsDataCache<TrampolineSocketDBWrapper> cache;
    spSocketDBWrapperMock = std::make_shared<MockSocketDBWrapper>();

    EXPECT_CALL(*spSocketDBWrapperMock, query(testing::_, testing::_))
        .Times(1)
        .WillOnce(testing::SetArgReferee<1>(nlohmann::json::object()));

    std::string agentId {"1"};

    // Try to get value from empty cache
    EXPECT_THROW(cache.getOsData(agentId), WdbDataException);

    // Set value in cache
    Os osData {.hostName = "hostName",
               .architecture = "architecture",
               .name = "name",
               .codeName = "codeName",
               .majorVersion = "majorVersion",
               .minorVersion = "minorVersion",
               .patch = "patch",
               .build = "build",
               .platform = "platform",
               .version = "version",
               .release = "release",
               .displayVersion = "displayVersion",
               .sysName = "sysName",
               .kernelVersion = "kernelVersion",
               .kernelRelease = "kernelRelease"};

    cache.setOsData(agentId, osData);

    // Get value from cache
    const auto osDataRetrieved = cache.getOsData(agentId);

    // Verify that the returned value is the same as the one set
    ASSERT_EQ(osDataRetrieved.hostName, osData.hostName);
    ASSERT_EQ(osDataRetrieved.architecture, osData.architecture);
    ASSERT_EQ(osDataRetrieved.name, osData.name);
    ASSERT_EQ(osDataRetrieved.codeName, osData.codeName);
    ASSERT_EQ(osDataRetrieved.majorVersion, osData.majorVersion);
    ASSERT_EQ(osDataRetrieved.minorVersion, osData.minorVersion);
    ASSERT_EQ(osDataRetrieved.patch, osData.patch);
    ASSERT_EQ(osDataRetrieved.build, osData.build);
    ASSERT_EQ(osDataRetrieved.platform, osData.platform);
    ASSERT_EQ(osDataRetrieved.version, osData.version);
    ASSERT_EQ(osDataRetrieved.release, osData.release);
    ASSERT_EQ(osDataRetrieved.displayVersion, osData.displayVersion);
    ASSERT_EQ(osDataRetrieved.sysName, osData.sysName);
    ASSERT_EQ(osDataRetrieved.kernelVersion, osData.kernelVersion);
    ASSERT_EQ(osDataRetrieved.kernelRelease, osData.kernelRelease);

    spSocketDBWrapperMock.reset();
}

TEST_F(OsDataCacheTest, TestDbQuery)
{
    spSocketDBWrapperMock = std::make_shared<MockSocketDBWrapper>();
    OsDataCache<TrampolineSocketDBWrapper> cache;

    // Create fake response
    nlohmann::json response = nlohmann::json::array();
    response.push_back(nlohmann::json::object());
    response.at(0)["hostname"] = "hostName";
    response.at(0)["architecture"] = "architecture";
    response.at(0)["os_name"] = "name";
    response.at(0)["os_codename"] = "codeName";
    response.at(0)["os_major"] = "majorVersion";
    response.at(0)["os_minor"] = "minorVersion";
    response.at(0)["os_patch"] = "patch";
    response.at(0)["os_build"] = "build";
    response.at(0)["os_platform"] = "platform";
    response.at(0)["os_version"] = "version";
    response.at(0)["os_release"] = "release";
    response.at(0)["os_display_version"] = "displayVersion";
    response.at(0)["sysname"] = "sysName";
    response.at(0)["version"] = "kernelVersion";
    response.at(0)["release"] = "kernelRelease";

    EXPECT_CALL(*spSocketDBWrapperMock, query(testing::_, testing::_))
        .Times(1)
        .WillOnce(testing::SetArgReferee<1>(response));

    std::string agentId {"1"};

    // Get value from cache
    auto osDataRetrieved = cache.getOsData(agentId);

    // Verify that the returned value is the one returned by the server
    ASSERT_EQ(osDataRetrieved.hostName, "hostName");
    ASSERT_EQ(osDataRetrieved.architecture, "architecture");
    ASSERT_EQ(osDataRetrieved.name, "name");
    ASSERT_EQ(osDataRetrieved.codeName, "codeName");
    ASSERT_EQ(osDataRetrieved.majorVersion, "majorVersion");
    ASSERT_EQ(osDataRetrieved.minorVersion, "minorVersion");
    ASSERT_EQ(osDataRetrieved.patch, "patch");
    ASSERT_EQ(osDataRetrieved.build, "build");
    ASSERT_EQ(osDataRetrieved.platform, "platform");
    ASSERT_EQ(osDataRetrieved.version, "version");
    ASSERT_EQ(osDataRetrieved.release, "release");
    ASSERT_EQ(osDataRetrieved.displayVersion, "displayVersion");
    ASSERT_EQ(osDataRetrieved.sysName, "sysName");
    ASSERT_EQ(osDataRetrieved.kernelVersion, "kernelVersion");
    ASSERT_EQ(osDataRetrieved.kernelRelease, "kernelRelease");
}

TEST_F(OsDataCacheTest, EmptyResponse)
{
    spSocketDBWrapperMock = std::make_shared<MockSocketDBWrapper>();
    OsDataCache<TrampolineSocketDBWrapper> cache;

    nlohmann::json queryResponse = nlohmann::json::array();

    EXPECT_CALL(*spSocketDBWrapperMock, query(testing::_, testing::_))
        .Times(1)
        .WillOnce(testing::SetArgReferee<1>(queryResponse));

    std::string agentId {"1"};

    // Try to get value from empty cache
    EXPECT_THROW(cache.getOsData(agentId), WdbDataException);
}

TEST_F(OsDataCacheTest, UnrecoverableExceptionOnDB)
{
    spSocketDBWrapperMock = std::make_shared<MockSocketDBWrapper>();
    OsDataCache<TrampolineSocketDBWrapper> cache;

    EXPECT_CALL(*spSocketDBWrapperMock, query(testing::_, testing::_))
        .Times(1)
        .WillOnce(testing::Throw(std::runtime_error("Error on DB")));

    std::string agentId {"1"};

    EXPECT_THROW(cache.getOsData(agentId), std::runtime_error);
    spSocketDBWrapperMock.reset();
}

TEST_F(OsDataCacheTest, RecoverableExceptionOnDB)
{
    spSocketDBWrapperMock = std::make_shared<MockSocketDBWrapper>();
    OsDataCache<TrampolineSocketDBWrapper> cache;

    EXPECT_CALL(*spSocketDBWrapperMock, query(testing::_, testing::_))
        .Times(1)
        .WillOnce(testing::Throw(SocketDbWrapperException("Warning on DB")));

    std::string agentId {"1"};

    EXPECT_THROW(cache.getOsData(agentId), WdbDataException);
    spSocketDBWrapperMock.reset();
}
